/*
 * CN_ATTACK_DUNGEON.cpp
 *
 *  Created on: 2013. 5. 21.
 *      Author: the2
 */

#include <math.h>

#include "../../User.h"
#include "../../NLogic/Drop.h"
#include "CN_ATTACK_DUNGEON.h"

#include "../../NLogic/Party.h"
#include "../../NLogic/Space.h"
#include "../../NLogic/Monster.h"
#include "../../NLogic/Battle.h"
#include "../../NLogic/Static.h"
#include "../../NLogic/Status.h"
#include "../../NResource/Static.h"

#include "../../NProcess/Static.h"
#include "../../NProcess/NCombat/WorldBoss.h"

#include "../../NResource/Static.h"
#include "../../NResource/ValidResource.h"

namespace ProjectZ { namespace NCommand { namespace NBattle {

void CN_ATTACK_DUNGEON::OnExecute(User * pUser, ::Network::Packet * pPacket)
{

	PREPARE_PACKET;

	NLogic::Space * pSpace = pUser->GetSpace();
	if (NULL == pSpace)
	{
		DEBUGLOG("CN_ATTACK_DUNGEON NULL == pSpace");
		return;
	}

	if (true == pSpace->IsWorldBoss())
	{
		DEBUGLOG("%s true == pSpace->IsWorldBoss()", __FUNCTION__);
		processWorldBoss(pUser, pPacket);
		return;
	}

	if (true == pSpace->IsSingleDungeon())
	{
		processSingleDungeion(pUser, pPacket);
		return;
	}

	if (true == pSpace->IsPartyDungeon())
	{
		processPartyDungeion(pUser, pPacket);
		return;
	}

	if ( pSpace->GetData()._spaceType == NLogic::Space::SpaceType::BATTLEROYAL )
	{
		DEBUGLOG("\t\t BATTLEROYAL");
		processBattleRoyal(pUser, pPacket);
		return;
	}

	if (pSpace->GetData()._spaceType == NLogic::Space::SpaceType::PVP_USER
			|| pSpace->GetData()._spaceType == NLogic::Space::SpaceType::NEW_PVP_ROOM)
	{
		DEBUGLOG("\t\t PVP_USER");
		processPVP(pUser, pPacket);
		return;
	}
}


void worldBoss_monster_attack_user()
{

}

void worldBoss_user_attack_monster()
{

}

void
CN_ATTACK_DUNGEON::processWorldBoss(User * pUser, ::Network::Packet * pPacket)
{
	NProcess::NCombat::WorldBoss * pWorldBoss = NProcess::Static::Instance().GetCombat().GetWorldBossCombat();
	if (NULL == pWorldBoss)
	{
		DEBUGLOG("%s NULL == pWorldBoss", __FUNCTION__);
		return;
	}

	pWorldBoss->Attack(pUser, pPacket);
}

void
CN_ATTACK_DUNGEON::processSingleDungeion(User * pUser, ::Network::Packet * pPacket)
{
	NProcess::NCombat::SingleDungeon * pSingeDungeon = NProcess::Static::Instance().GetCombat().GetSingleDungeonCombat();
	if (NULL == pSingeDungeon)
	{
		DEBUGLOG("%s NULL == pSingeDungeon", __FUNCTION__);
		return;
	}

	pSingeDungeon->Attack(pUser, pPacket);
}

void
CN_ATTACK_DUNGEON::processPartyDungeion(User * pUser, ::Network::Packet * pPacket)
{
	NProcess::NCombat::PartyDungeon * pPartyDungeon = NProcess::Static::Instance().GetCombat().GetPartyDungeonCombat();
	if (NULL == pPartyDungeon)
	{
		DEBUGLOG("%s NULL == pPartyDungeon", __FUNCTION__);
		return;
	}

	pPartyDungeon->Attack(pUser, pPacket);
}

void
CN_ATTACK_DUNGEON::processBattleRoyal(User * pUser, ::Network::Packet * pPacket)
{
/*
	// 던전에서 공격
	CN_ATTACK_DUNGEON	0x0515
	{
		u8	u8effect;		// 데미지 이펙트 출력 여부(0 없음 , 1 있음)
		u32	damage_value;	// Damage value(0 : 공격 실패, 0보다 큰경우 공격 성공)
		u32	defender_gid;	// DEFENDER GID
		u8	defender_ismonster;	// DEFENDER의 몬스터여부(0:주인공 1:몬스터)
		u16	degree;			방향 벡터(보고있는 방향 0 : RIGHT, 90 : DOWN..)
		u8	statetype;		// 상태이상여부 상위 4bit / 백어택여부 하위4bit
		u8	ismonster;		// 공격자의 몬스터여부 (0 : 주인공 1: 몬스터)
		u32	attacker_gid;	// 공격자 GID
		u8 attack_type;		// 공격타입 0: 일반공격 성공 1: miss 2: block
	}

	SN_ATTACK_DUNGEON	0x0516
	{
		u8	u8effect;		// 데미지 이펙트 출력 여부(0 없음 , 1 있음)
		u32	damage_value;	// Damage value(0 : 공격 실패, 0보다 큰경우 공격 성공)
		u32	defender_gid;	// DEFENDER GID
		u8	defender_ismonster;	// DEFENDER의 몬스터여부(0:주인공 1:몬스터)
		u32	defender_hp;	// DEFENDER의 데미지 반영 후 HP
		u16	degree;			방향 벡터(보고있는 방향 0 : RIGHT, 90 : DOWN..)
		u8	ismonster;		// 공격자의 몬스터여부 (0 : 주인공 1: 몬스터)
		u32	attacker_gid;	// 공격자 GID
		u8 attack_type;		// 공격타입 0: 일반공격 성공 1: miss 2: block
	}
*/

	char effect;
	int damage_value;
	uint32_t defender_gid;
	int8_t defender_ismonster;
	int16_t degree;
	int8_t statetype;
	int8_t ismonster;
	uint32_t attacker_gid;
	uint8_t attack_type;

	*pPacket >> effect >> damage_value >> defender_gid;
	*pPacket >> defender_ismonster >> degree >> statetype >> ismonster >> attacker_gid >> attack_type;

	if (1 == ismonster )
	{
		DEBUGLOG("\n\n\t %s %d ismonster[%d] ATTACK ERROR \n\n", __FUNCTION__, __LINE__, ismonster);
		return;
	}

	DEBUGLOG("\n\n\t %s %d defender_[%d] attacker_[%d]\n\n", __FUNCTION__, __LINE__, defender_gid, attacker_gid);

	NLogic::Space *pSpace = pUser->GetSpace();
	if ( NULL == pSpace )
	{
		DEBUGLOG("%s %d space is null", __FUNCTION__, __LINE__);
		return;
	}

	if ( ismonster == false )
	{
		pUser->GetBattle()->GetData().degree = degree;
	}

	if ( defender_gid <= 0 )
	{
		DEBUGLOG("CN_ATTACK_DUNGEON, wrong defender_gid[%d]", defender_gid);
		return;
	}

	NLogic::Battle * pAttacker = pSpace->FindBattle(attacker_gid);
	if ( NULL == pAttacker && 4 != ismonster )
	{
		DEBUGLOG("\t========== CN_ATTACK_DUNGEON calc PK point, pAttacker is NULL, attacker_gid=%d", attacker_gid );
		return;
	}

	if (NULL != pAttacker && 0 >= pAttacker->GetData().HP)
		return;

	NLogic::Battle * pDefender = pSpace->FindBattle(defender_gid);
	if ( NULL == pDefender )
	{
		DEBUGLOG("\t========== CN_ATTACK_DUNGEON calc PK point, pDefender is NULL, defender_gid=%d", defender_gid );
		return;
	}

	if ( 0 >= pDefender->GetData().HP )
		return;

	NResource::ValidResource::Flyweight * pFly = NResource::Static::Instance().GetValid()->GetFly(pUser->GetCharacterInfo().level);
	if (NULL == pFly)
	{
		DEBUGLOG("%s NULL == pFly", __FUNCTION__);
		return;
	}

	if (pFly->_DAMAGES < damage_value)
	{
		DEBUGLOG("CHEATUSER %s NULL == pFly GID : %d", __FUNCTION__, pUser->GetUserSeq());
		return;
	}

	if (damage_value >= pDefender->GetData().HP)
	{
		DEBUGLOG("CN_ATTACK_DUNGEON defender user_seq : %d", defender_gid);
		pDefender->GetData().HP = 0;
		pDefender->GetData().rebirthIntervalTime = time(NULL) + 3;
	}
	else
		pDefender->GetData().HP -= damage_value;

	::Network::Packet * pAck = ::Network::GETPACKETPOOL()->SEND_NEW();
	*pAck << effect;
	*pAck << damage_value;
	*pAck << defender_gid;
	*pAck << defender_ismonster;
	*pAck << pDefender->GetData().HP;
	*pAck << degree;
	*pAck << ismonster;
	*pAck << attacker_gid;
	*pAck << attack_type;

	pAck->MakePacket(CMD_SN_ATTACK_DUNGEON);
	pSpace->SendAll(pAck);

	// 도트 데미지
	if (4 == ismonster)
	{
		TESTLOG("%s %d defender_[%d] attacker_[%d] damage_value %d HP %d",
				__FUNCTION__, __LINE__, defender_gid, attacker_gid, damage_value, pDefender->GetData().HP);
		return;
	}

	int damage_state = (statetype << 3 ) >> 7;	// 상태이상여부
	int back_attack = statetype & 1;			// 백어택여부

	int ap_this_turn = 0;
	int attack_set = 0;


	if( damage_state == 0 )
	{
		::Logger::GETLOG()->ToFile(::Logger::DEBUG, "\t========== CN_ATTACK_DUNGEON user->user");

		// back attack
		if ( back_attack == 1 )
		{
			ap_this_turn += 2;
			attack_set |= 4;

			pAttacker->GetData().back_attack++;
		}

		if (pDefender->GetData().HP == 0)
		{
			User * pDefendUser = pDefender->GetUser();
			if (NULL != pDefendUser)
			{
				NLogic::Status * pStatus = pDefendUser->GetStatus();
				if (NULL != pStatus)
				{
					NLogic::Status::Data * pData = pStatus->GetStausData(NLogic::Status::StatusType::BATTLEPET_REBIRTH);
					if (NULL != pData && true == pData->_bRegist)
					{
						pDefender->GetData().HP = pDefender->GetData().HP_MAX * pData->_value / 100;

						::Network::Packet * rebirth = ::Network::GETPACKETPOOL()->SEND_NEW();
						* rebirth << static_cast<uint8_t>(0);
						* rebirth << pUser->GetUserSeq();
						* rebirth << pDefender->GetData().HP;
						rebirth->MakePacket(CMD_SC_REBIRTH_DIRECT);
						pSpace->SendAll(rebirth);

						pData->Clear();
					}
				}
			}
		}

		if ( pDefender->GetData().HP == 0 )
		{
			// kill count add
			pAttacker->GetData().kill_count++;

			// dead count add
			pDefender->GetData().dead_count++;

			// last attack
			ap_this_turn += 3;
			attack_set |= 8;

			pSpace->GetData()._aliveUserCount--;
			if ( pSpace->GetData()._aliveUserCount < 0 )
				pSpace->GetData()._aliveUserCount = 0;

			// first kill
			if ( pSpace->GetData()._killUserCount == 0 )
			{
				ap_this_turn += 5;
				pSpace->GetData()._killUserCount++;
				attack_set |= 2;

				//
				pAttacker->GetData().first_kill_count++;
			}

			// all kill
			if ( pSpace->GetData()._aliveUserCount == 1 )
			{
				ap_this_turn += 3;
				attack_set |= 32;

				pAttacker->GetData().all_kill_count++;
			}
		}

		if (0 < damage_value)
		{
			// high scorer
			int my_ap = pAttacker->GetData().attack_point;
			int opp_ap = pDefender->GetData().attack_point;
			if ( opp_ap > my_ap )
			{
				ap_this_turn *= 2;
				attack_set |= 16;
			}

			// normal, always +1
			ap_this_turn++;
			attack_set |= 1;

			User * pAttackUser = pAttacker->GetUser();
			if (NULL != pAttackUser)
				pAttackUser->GetCharacterInfo().ap_current += ap_this_turn;

			pAttacker->GetData().ap_this_game += ap_this_turn;
		}
	}

	pAttacker->GetData().attack_point = pAttacker->GetData().ap_this_game;

	::Network::Packet * pNoti = ::Network::GETPACKETPOOL()->SEND_NEW();
	* pNoti << attacker_gid;	//u16	u16gid;
	* pNoti << static_cast<int16_t>(ap_this_turn);	//s16	s16pk_point_variation;		// 약탈포인트 증감
	* pNoti << static_cast<uint16_t>(pAttacker->GetData().ap_this_game);	//u16	u16current_pk_point;		// 현재 약탈포인트
	* pNoti << static_cast<uint8_t>(0);	//u8	u8current_pk_rank;		// 현재 순위 약탈포인트 기준
	* pNoti << static_cast<uint8_t>(attack_set);
	pNoti->MakePacket(CMD_SN_PK_POINT);

	pSpace->SendAll(pNoti);
}

void
CN_ATTACK_DUNGEON::processPVP(User * pUser, ::Network::Packet * pPacket)
{
	NProcess::NCombat::PVP * pPVP = NProcess::Static::InstancePtr()->GetCombat().GetPVPCombat();
	if (NULL == pPVP)
	{
		DEBUGLOG("%s : pPVP is NULL", __FUNCTION__);
		return;
	}

	pPVP->Attack(pUser, pPacket);
}

} /*NBattle*/ } /*NCommand*/ } /*ProjectZ*/
